package com.newx.test.open.xyz;

import java.io.*;
import java.net.Socket;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

/**
 * @Classname MCUClient
 * @Description TODO 客户端代码（Java客户端）
 * @Date 2025/1/7 15:53
 * @Created by yxl
 */
public class MCUClient {

    public static final int BUFFER_SIZE = 1024; // 每个数据包的最大大小
    public static final int TIMEOUT_MS = 5000;  // 超时时间（毫秒）

    public static void main(String[] args) {
        String host = "127.0.0.1"; // 服务端IP地址
        int port = 5000; // 服务端监听的TCP端口

        try (Socket socket = new Socket(host, port);
             DataInputStream inStream = new DataInputStream(socket.getInputStream());
             DataOutputStream outStream = new DataOutputStream(socket.getOutputStream())) {

            // 设置读取超时
            socket.setSoTimeout(TIMEOUT_MS);

            // 发送初始消息给服务端
            outStream.writeUTF("Request file");
            outStream.flush(); // 确保消息发送出去
            System.out.println("请求文件...");

            // 接收文件并保存到本地
            receiveFile(inStream, outStream, "F:\\Desktop\\received_example.bin");

        } catch (UnknownHostException e) {
            System.err.println("未知主机: " + e.getMessage());
            e.printStackTrace();
        } catch (IOException e) {
            System.err.println("IO异常: " + e.getMessage());
            e.printStackTrace();
        }
    }

    private static void receiveFile(DataInputStream inStream, DataOutputStream outStream, String savePath) throws IOException {
        List<byte[]> packets = new ArrayList<>();
        long totalSize = 0;
        String fileName = "";

        // 接收文件信息帧
        try {
            Frame fileInfoFrame = Frame.parseFromInputStream(inStream);
            if (fileInfoFrame.getCommandType() == 0) { // 文件信息
                ByteBuffer bb = ByteBuffer.wrap(fileInfoFrame.getData());
                totalSize = bb.getLong();
                byte[] fileNameBytes = new byte[fileInfoFrame.getData().length - 8];
                bb.get(fileNameBytes);
                fileName = new String(fileNameBytes, StandardCharsets.UTF_8).trim();
                System.out.println("准备接收文件: " + fileName + " (" + totalSize + " bytes)");

                // 发送确认
                outStream.writeUTF("ACK 0");
                outStream.flush();
            } else {
                throw new IOException("预期文件信息帧，但收到其他类型的帧");
            }
        } catch (IOException e) {
            System.err.println("接收文件信息帧时发生IO异常: " + e.getMessage());
            e.printStackTrace();
            return;
        }

        // 接收数据包帧
        int packetNumber = 0;
        while (true) {
            try {
                Frame frame = Frame.parseFromInputStream(inStream);
                if (frame.getCommandType() == 1) { // 数据包
                    if (frame.getSequenceNumber() == packetNumber) {
                        packets.add(frame.getData());
                        outStream.writeUTF("ACK " + packetNumber);
                        outStream.flush();
                        System.out.println("确认收到数据包 " + packetNumber);
                        packetNumber++;
                    } else {
                        System.out.println("忽略非顺序的数据包 " + frame.getSequenceNumber());
                        outStream.writeUTF("NACK " + frame.getSequenceNumber());
                        outStream.flush();
                    }
                } else {
                    System.out.println("忽略非数据包帧");
                }

                // 检查是否接收完毕
                if (totalSize > 0 && packets.stream().mapToInt(arr -> arr.length).sum() >= totalSize) {
                    break;
                }
            } catch (IOException e) {
                System.err.println("接收数据包时发生IO异常: " + e.getMessage());
                e.printStackTrace();
                break;
            }
        }

        // 将所有数据包合并成一个文件
        try (FileOutputStream fos = new FileOutputStream(savePath);
             BufferedOutputStream bos = new BufferedOutputStream(fos)) {
            for (byte[] packet : packets) {
                bos.write(packet);
            }
        } catch (IOException e) {
            System.err.println("写入文件时发生IO异常: " + e.getMessage());
            e.printStackTrace();
        }

        System.out.println("文件接收完成并保存为: " + savePath);
    }
}
